package com.example.spring5.gft;


import java.util.*;
import java.util.stream.Collectors;

/**
 * Background
 * <p>
 * Words are made of 52 letters, including 26 capital letters and 26 small letters, and two types of punctuations, hyphen (-) and apostrophe (’).
 * <p>
 * A letter's weight is defined by its distance to letter a in alphabet. So weight(a) = 0, weight(b) = 1 ... weight(z) = 25.
 * <p>
 * A capital letter's weight equals to its small letter. So weight(a) = weight(A).
 * <p>
 * All punctuations have no weight.
 * <p>
 * A word's weight is the sum of each letter and punctuation. So weight(cat) = 21, weight(I’m) = 20.
 * <p>
 * A prime word is a word whose weight is a prime number, e.g. be because weight(be) = 5.
 * <p>
 * Question
 * <p>
 * Could you find the top 3 heaviest prime words appeared in If (by Rudyard Kipling)?
 *
 * @Author: Hu Rui
 * @Date: 2021/11/16 14:50
 */
public class HeaviestPrimeWords {

    /**
     * 获取文章中权重最大的三个素数集合
     *
     * @param str
     * @return
     */
    public static List<String> findTop3HeaviestPrimeWords(String str) {
        if (str == null || str.length() == 0) {
            return null;
        }
        str = str.replaceAll("\n", "");
        Set<String> words = new HashSet<>(Arrays.asList(str.split(" ")));
        if (words == null || words.isEmpty()) {
            return null;
        }
        List<String> ansList = new ArrayList<>();
        //防止扩容 这里不考虑大数量情况
        Map<String, Integer> map = new HashMap<>((int) (words.size() / 0.75 + 1));
        words.stream().forEach(word -> {
            int weight = getWordWeight(word);
            if (isPrime(weight)) {
                map.put(word, weight);
            }
        });
        Collection<Integer> values = map.values();
        List<Integer> top3 = values.stream().distinct().sorted((o1, o2) -> o2 - o1).limit(3).collect(Collectors.toList());
        Set<Map.Entry<String, Integer>> entries = map.entrySet();
        for (Map.Entry<String, Integer> entry : entries) {
            if (top3.contains(entry.getValue())) {
                ansList.add(entry.getKey());
            }
        }
        return ansList;
    }


    /**
     * 获取某个词的权重
     *
     * @param word
     * @return
     */
    public static int getWordWeight(String word) {
        //无效字符
        if (word == null || word.length() == 0) {
            return -1;
        }
        //去掉无权重字符
        word = word.replaceAll("-", "").replaceAll("’", "");
        //统一转为大写 ASCII码值 范围[65,90]
        word = word.toUpperCase();
        char[] letters = word.toCharArray();
        int weight = 0;
        for (char letter : letters) {
            int letterWeight = letter - 65;
            //如果含有非法字符需要结束，可以在else中直接return -1;
            if (letterWeight >= 0 && letterWeight <= 25) {
                weight += letterWeight;
            }
        }
        return weight;
    }

    /**
     * 判断一个数是否为素数
     *
     * @param x
     * @return
     */
    public static boolean isPrime(int x) {
        if (x < 2) {
            return false;
        }
        int n = (int) Math.sqrt(x);
        for (int i = 2; i <= n; ++i) {
            if (x % i == 0) {
                return false;
            }
        }
        return true;
    }
}
